﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace ParameterWriter
{
    /// <summary>
    /// Klasse Zur Verwaltung von Parametern
    /// </summary>
    public class ParameterWriter
    {
        private ParameterCollection parameters;
        /// <summary>
        /// ParameterCollection mit gespeicherten Parametern
        /// </summary>
        public ParameterCollection Parameters
        {
            get
            {
                return parameters;
            }
            set
            {
                parameters = value;
            }
        }

        private String fileName;
        /// <summary>
        /// Dateiname für gespeicherten Parameter
        /// </summary>
        public String FileName
        {
            get
            {
                return fileName;
            }
            set
            {
                fileName = value;
            }
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        public ParameterWriter()
        {
            Parameters = new ParameterCollection();
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="fileName">Dateiname mit gespeicherten Parametern</param>
        public ParameterWriter(String fileName)
        {
            Parameters = new ParameterCollection();
            Load(fileName);
        }


        /// <summary>
        /// Speichert die Parameter unter dem in "FileName" angegeben Dateinamen
        /// </summary>
        /// <exception cref="NoFileNameException">Wird geworfen, wenn kein Dateiname angegeben wurde.</exception>
        public void Save()
        {
            if (FileName == String.Empty)
            {
                throw new NoFileNameException();
            }
            else
            { 
                Save(FileName); 
            }
        }

        /// <summary>
        /// Speichert die Parameter im übergebenen Dateinamen
        /// </summary>
        /// <param name="fileName">Ort zum Speichern der Parameter</param>
        public void Save(String fileName)
        {
            FileName = fileName;
            XmlDocument document = new XmlDocument();
            XmlNode parametersNode = document.CreateElement("Parameters"); //Root - Element erzeugen
            foreach (Parameter myParameter in Parameters) 
            {
                XmlNode parameter = document.CreateElement("Parameter");
                
                XmlAttribute nameAttribute = document.CreateAttribute("Name");
                nameAttribute.Value = myParameter.Name;
                parameter.Attributes.Append(nameAttribute);

                XmlAttribute typeAttribute = document.CreateAttribute("Type");
                typeAttribute.Value = myParameter.Type.ToString();
                parameter.Attributes.Append(typeAttribute);

                parameter.InnerText = myParameter.Value;
                parametersNode.AppendChild(parameter);
            }
            document.AppendChild(parametersNode);

            document.Save(fileName);
        }

        /// <summary>
        /// Läd die Parameter aus dem angegebenen Dateinamen
        /// </summary>
        /// <param name="fileName">Ort für Parameter</param>
        /// <exception cref="ParameterExistsException">Wird geworfen wenn ein Parameter(-Name) bereits existiert.</exception>
        public void Load(String fileName)
        {
            FileName = fileName;
            XmlDocument document = new XmlDocument();
            document.Load(fileName);

            XmlNode parametersNode = document.DocumentElement;
            ParameterCollection tmpCollection = new ParameterCollection();
            foreach (XmlNode parameter in parametersNode.ChildNodes)
            {
                if (parameter.Name == "Parameter")//Parameterknoten
                {
                    Parameter newParameter = new Parameter();
                    newParameter.Name = parameter.Attributes["Name"].Value;
                    newParameter.Type = Type.GetType(parameter.Attributes["Type"].Value, false);
                    newParameter.Value = parameter.InnerText;

                    tmpCollection.Add(newParameter);
                }
            }

            Parameters = tmpCollection;
        }

        /// <summary>
        /// Gibt den Parameter mit übergebenem Namen zurück
        /// </summary>
        /// <param name="name">Name des Parameters</param>
        /// <returns>Zurückgegebener Parameter</returns>
        public Parameter GetParameter(String name)
        {
            return Parameters[name];
        }

        /// <summary>
        /// Gibt den Parameter mit übergebenem Index zurück
        /// </summary>
        /// <param name="index">Index des Parameters</param>
        /// <returns>Zurückgegebener Parameter</returns>
        public Parameter GetParameter(int index)
        {
            return Parameters[index];
        }

        /// <summary>
        /// Fügt einen neuen Parameter zur Collection hinzu
        /// </summary>
        /// <param name="parameter">hinzuzufügender Parameter</param>
        public void AddParameter(Parameter parameter) 
        {
            Parameters.Add(parameter);
        }


        /// <summary>
        /// Entfernt einen Parameter aus der Collection
        /// </summary>
        /// <param name="parameter">zu entfernender Parameter</param>
        public void RemoveParameter(Parameter parameter)
        {
            Parameters.Remove(parameter);
        }

        /// <summary>
        /// Entfernt einen Parameter mit einem bestimmten Namen aus der Collection
        /// </summary>
        /// <param name="name">Name des zu entfernenden Parameters</param>
        public void RemoveParameter(String name)
        {
            Parameters.RemoveAt(name);
        }

        /// <summary>
        /// Setzt einen Wert für einen Parameter neu
        /// </summary>
        /// <param name="name">Name des Parameters</param>
        /// <param name="value">neuer Wert</param>
        public void SetParameter(String name, String value)
        {
            for (int i = 0; i < Parameters.Count; i++)
            {
                if (Parameters[i].Name == name)
                {
                    Parameters[i] = new Parameter()
                    {
                        Name = Parameters[i].Name,
                        Type = Parameters[i].Type,
                        Value = value
                    };
                    break;
                }
            }
        }
    }

    /// <summary>
    /// Struktur zum Speichern von Parametern
    /// </summary>
    public struct Parameter
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="value"></param>
        public Parameter(String name, Type type, String value)
        {
            Name = name;
            Value = value;
            Type = type;
        }
        public String Name;
        public String Value;
        public Type Type;
    }

    /// <summary>
    /// ParameterCollection, abgeleitet von List
    /// </summary>
    public class ParameterCollection : List<Parameter>
    {
        /// <summary>
        /// Gibt den Parameter mit angegebenem Namen zurück
        /// </summary>
        /// <param name="name">Name des Parameters</param>
        /// <returns>Parameter</returns>
        public Parameter this[String name]
        {
            get
            {
                return this.Where(w => w.Name == name).First();
            }
        }
        /// <summary>
        /// Fügt einen neuen Parameter zur Collection hinzu
        /// </summary>
        /// <param name="param">neuer Parameter</param>
        /// <exception cref="ParameterExistsException">Wird geworfen wenn ein Parameter(-Name) bereits existiert.</exception>
        public new void Add(Parameter param)
        {
            if (this.Select(s => s.Name).Contains(param.Name))
            {
                throw new ParameterExistsException();
            }
            else
            {
                base.Add(param);
            }
        }


        /// <summary>
        /// Entfernt einen Parameter mit einem bestimmten Namen
        /// </summary>
        /// <param name="name">Name des zu entfernenden Parameters</param>
        public void RemoveAt(String name)
        {
            foreach (var parameter in this)
            {
                if (parameter.Name == name)
                {
                    this.Remove(parameter);
                    break;
                }
            }
        }

    }

    /// <summary>
    /// Exception - Klasse
    /// </summary>
    public class ParameterExistsException : Exception
    {
        public ParameterExistsException():base("Parameter already exists, parameter name must be unique.")
        {
        }
    }

    /// <summary>
    /// Exception - Klasse
    /// </summary>
    public class NoFileNameException : Exception
    {
        public NoFileNameException()
            : base("No Filename, please specify filename first, or use Save(String fileName) - method.")
        {
        }
    }
}


